// Ofri Keidar 302933981 group 05

/**
 * This class implements Controller interface. The class performs transactions on system's data, using the database 
 */

package controller;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Map;
import java.util.HashMap;
import java.util.TreeMap;
import model.Model;
import model.DataBase;
import model.apartment.*;
import model.apartment.Apartment.BasicExtractor;
import model.apartment.GardenApartment.Extractor;
import model.building.Building;

public class Transactor implements Controller {
	
	// member
	private Model dataBase; // system's database
		
	// methods
	public Transactor() throws IOException, ClassNotFoundException {		
		
		dataBase = new DataBase();  // allocate a new database object
	}

	/**
	 * Gets all flats in database, scans them and returns a sorted map holding only the vacant flats- key is flat 
	 * and value is String containing flat number in its building and its building address.
	 * Flats are sorted in ascending order
	 */
	@Override
	public Map<Flat, String> getVacantFlats() {

		// get all flats in system
		Map<Flat, String> flatsMap = dataBase.getAllFlats();
		
		// initialize sorted vacant flats map
		TreeMap<Flat, String> vacantFlats = new TreeMap<Flat, String>();
		
		// scan list and find all vacant
		for (Flat currFlat : flatsMap.keySet()) {
			// check if current flat is vacant
			if (currFlat.isVacant()) {
				// add flat
				vacantFlats.put(currFlat, flatsMap.get(currFlat));				
			}
		}
		
		// return sorted map
		return vacantFlats;
	}

	/**
	 * Uses getVacantFlats() to get all vacant flats in system. Scans these flats and returns a sorted map holding 
	 * only those which have room number as given room number- key is flat and value is String
	 * containing flat number in its building and its building address.
	 * Flats are sorted in ascending order 
	 */
	@Override
	public Map<Flat, String> getVacantFlats(int roomNum) {

		// get all vacant flats in system
		Map<Flat, String> vacantFlats = getVacantFlats();
		
		// initialize sorted flats map
		TreeMap<Flat, String> outputFlats = new TreeMap<Flat, String>();
		
		// find the flats with given room number
		for (Flat currFlat : vacantFlats.keySet()) {
			// check if current flat is vacant
			if (currFlat.getRoomNum() == roomNum) {
				// add flat
				outputFlats.put(currFlat, vacantFlats.get(currFlat));				
			}
		}
		
		// return sorted map
		return outputFlats;		
	}

	/**
	 * Uses getFlats to find all flats in the building at given address. If there are not any in there, returns an empty map.
	 * Otherwise, scans building's flats and finds the vacant with given room number.
	 * Returns a map with these flats- key is flat and value is String containing flat number in its building and its building address.
	 * Flats are sorted in ascending order 
	 */
	@Override
	public Map<Flat, String> getVacantFlats(int roomNum, String stName, int bldNumber) {
		
		// get all flats at given address
		Map<Flat, String> flatsMap = getFlats(stName, bldNumber);
		
		// check if such flats exist
		if (flatsMap.isEmpty()) {
			// no flats returned
			return new HashMap<Flat, String>();			
		}
		
		// initialize sorted flats map
		TreeMap<Flat, String> outputFlats = new TreeMap<Flat, String>();
		
		// scan flats at given address and search the vacant with given room number
		for (Flat currFlat : flatsMap.keySet()) {			
			// check if current flat is vacant and with given room number
			if (currFlat.isVacant() && currFlat.getRoomNum() == roomNum) {
				// add flat 
				outputFlats.put(currFlat, flatsMap.get(currFlat));
			}
		}

		// returned sorted map
		return outputFlats;				
	}

	/**
	 * Asks database for building at given address. If no such building exists, returns an empty map.
	 * Otherwise, scans building's apartments and finds the flats.
	 * Returns a map with these flats- key is flat and value is String containing flat number in its building and its building address.
	 * Flats are sorted in ascending order  
	 */
	@Override
	public Map<Flat, String> getFlats(String stName, int bldNumber) {

		// get building at given address
		Building bldToCheck = dataBase.getBuilding(stName, bldNumber);
		
		// check if such building exists
		if (bldToCheck == null) {
			// there is no building at given address
			return new HashMap<Flat, String>();			
		}
						
		// initialize hash map to hold flats
		Map<Flat, String> flatsMap = new HashMap<Flat, String>();
		
		// find all vacant flats at wanted building
		for (Apartment currApartment : bldToCheck.getApartments()) {
			int apartmentIndx = 1; // initialize index of first apartment		
			// check if current apartment is a flat
			if ( (currApartment instanceof Flat)) {
				
				// compose value string
				String valString = "Address: " + bldNumber + " " + stName; // compose complete address
				valString = valString + ", Apartment #: " + apartmentIndx; // add apartment's number

				// add current apartment to list
				flatsMap.put((Flat)currApartment, valString);
			}
			++apartmentIndx; // move to next apartment- increment index
		}
		
		// sort map
		Map<Flat, String> sortedMap = new TreeMap<Flat, String>();
		sortedMap.putAll(flatsMap);
		// returned sorted map
		return sortedMap;
	}

	/**
	 * Gets all flats in database, sorts them in ascending order and returns sorted map.
	 * In the map, key is flat and value is String containing flat number in its building and its building address.
	 */
	@Override
	public Map<Flat, String> getFlats() {
		
		// initialize sorted map
		TreeMap<Flat, String> output = new TreeMap<Flat, String>();
		// sort all flats in system
		output.putAll(dataBase.getAllFlats());
		// return sorted map
		return output;
	}
	
	/**
 	 * Gets all penthouses in database, sorts them in ascending order and returns sorted map.
	 * In the map, key is penthouse and value is String containing penthouse number in its building and its building address.
	 */
	@Override
	public Map<Penthouse, String> getPenthouses() {
				
		// initialize sorted map
		TreeMap<Penthouse, String> output = new TreeMap<Penthouse, String>();
		// sort all penthouses in system
		output.putAll(dataBase.getAllPenthouses());
		// return sorted map
		return output;
	}

	/**
 	 * Gets all garden apartments in database, sorts them in ascending order and returns sorted map.
	 * In the map, key is garden apartment and value is String containing garden apartment number in its building and its building address.
	 */
	@Override
	public Map<GardenApartment, String> getGardenApartments() {
		
		// initialize sorted map
		TreeMap<GardenApartment, String> output = new TreeMap<GardenApartment, String>();
		// sort all garden apartment in system
		output.putAll(dataBase.getAllGardenAprtmnts());
		// return sorted map
		return output;
	}

	/**
	 * Uses database to get map of vacant apartments, and returns number of elements in this map
	 */
	public int getVacantAprtsNum() {
		
		// get all vacant apartments in system and returns their amount
		return dataBase.getVacantApartments().size();
	}
	
	public int getVacantAprtsNum(String stName, int bldNumber) {
		
		// initialize counter of vacant apartments
		int vacantAprtCounter = 0;
		
		// check if there is a building at given address
		Building bldToSearch = dataBase.getBuilding(stName, bldNumber);
		if (bldToSearch == null) {
			return vacantAprtCounter;
		}
				
		// building exists, count how many vacant apartments are in building		 
		for (Apartment currApartment : bldToSearch.getApartments()) {
			// check if current apartment is vacant
			if (currApartment.isVacant()) {
				// found a vacant apartment, increment counter
				++vacantAprtCounter;
			}
		}
		
		// return how many vacant apartments were found
		return vacantAprtCounter;
	}
	
	/**
	 * Uses apartments' Extractor objects to check if parameters were given in the correct format.
	 * If format is incorrect, an empty map is returned. Otherwise, returns a map of parameters
	 */
	public Map<String, String> parseParameters(String apartmentType, String[] parameters) {
		
		// parse parameters according to given type
		if (apartmentType.equals("Flat") || apartmentType.equals("Penthouse")) {
			BasicExtractor extractor = new BasicExtractor();
			return extractor.parseParameters(parameters);
		}
		Extractor extractor = new Extractor();
		return extractor.parseParameters(parameters);		
	}
	
	/**
	 * Gets parameters of all apartments and new building's address. If there already another building at given address,
	 * a new building is not created and False is returned. Otherwise, creates a new building with given details and True is returned
	 */
	//public boolean addBuilding(String[] apartmentTypes, LinkedList< Map<String, String> > parameterList, String stName, int bldNumber) {
	public boolean addBuilding(LinkedList<String> apartmentTypes, LinkedList< Map<String, String> > parameterList, String stName, int bldNumber) {
		
		// check if there is not already another building at given address
		if (dataBase.getBuilding(stName, bldNumber) != null) {
			// address is occupied, cannot add a new building there
			return false;
		}
		
		// add new building at given address
		dataBase.addNewBuilding(apartmentTypes, parameterList, stName, bldNumber);
		return true;
	}
	
	/**
	 * Asks database for building at given address. If there is not any, returns False.
	 * Otherwise, checks if there is an apartment with given number in the building. If there is not, returns False.
	 * If there is an apartment, sets its resident name as given name and returns True
	 */
	public boolean updateResident(String stName, int bldNum, int apartmentNum, String newResident) {
		
		// get building at given address
		Building bldToSearch = dataBase.getBuilding(stName, bldNum);
		if (bldToSearch == null) {
			// no building at given address
			return false;
		}
		
		// set resident of apartment with given number
		Apartment aprtmentToChange = bldToSearch.getApartment(apartmentNum);
		if (aprtmentToChange == null) {
			// no apartment with given address in the building
			return false;
		}
		
		// wanted apartment exists, set new resident
		aprtmentToChange.setName(newResident);
		return true;
	}
	
	/**
	 * Instructs database to shutdown.
	 * Returns True if shut down was carried out with no problem, False if a problem had occurred
	 */
	public boolean shutDown() {
		// instruct database to shut down
		return dataBase.shutDownSystem();
	}

}
